package com.cheer.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.cheer.bo.LotteryBO;
import com.cheer.bo.PrizeBO;
import com.cheer.common.ResultEnum;
import com.cheer.dao.*;
import com.cheer.entity.ExMatchAwardEntity;
import com.cheer.entity.ExMatchAwardRecordEntity;
import com.cheer.entity.ExMatchUserEntity;
import com.cheer.exception.ServiceException;
import com.cheer.service.LotteryService;
import com.cheer.util.ExcelsUtil;
import com.cheer.vo.PrizeVO;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author cheer
 */
@Service
@Slf4j
public class LotteryServiceImpl implements LotteryService, ApplicationListener<ApplicationReadyEvent> {

    @Autowired
    private ThreadPoolTaskScheduler taskScheduler;

    @Autowired
    private ExMatchAwardDao exMatchAwardDao;

    @Autowired
    private ExMatchAwardRecordDao exMatchAwardRecordDao;

    @Autowired
    private ExMatchUserDao exMatchUserDao;

    @Autowired
    private PrizeCacheDao prizeCacheDao;

    @Autowired
    private LotteryCacheDao lotteryCacheDao;

    @Value("${lottery.interval}")
    private Integer interval;

    static final String ANNOUNCEMENT_PATTERN = "恭喜 %s 获得 %s";

    /**
     * 随机池
     */
    private Integer MAX_RANDOM = 100;
    /**
     * 放入奖品池的概率 10%
     */
    private Integer PROBABILITY = 10;


    private Map<String, Integer> dateMap = new HashMap<>();

    @Override
    @Transactional(rollbackFor={Exception.class})
    public void onApplicationEvent(ApplicationReadyEvent event) {
        taskScheduler.scheduleWithFixedDelay(() -> {
            try {
                List<ExMatchAwardEntity> awardList = exMatchAwardDao.selectList(new QueryWrapper<>()).stream()
                        .filter(award -> award.getStock() > 0)
                        .sorted(Comparator.comparingInt(ExMatchAwardEntity::getStock).reversed())
                        .collect(Collectors.toList());

                for (ExMatchAwardEntity award : awardList) {

                    // 当前时间小于开始时间 不投放
                    if( award.getStartPut() != null
                            && new Date().compareTo(award.getStartPut()) == -1 ){
                        continue;
                    }
                    // 当前时间大于结束时间 不投放
                    if( award.getEndPut() != null
                            && new Date().compareTo(award.getEndPut()) == 1 ){
                        continue;
                    }

                    log.info("资格调用ID值" + award.getId());
                    log.info("资格调用ID值" + award.getId());

                    String dateKey = DateUtil.format(new Date(), "yyyy-MM-dd") + "_" + award.getId();
                    Integer maxNumber = dateMap.get(dateKey);
                    if(maxNumber == null){
                        maxNumber = 0;
                    }
                    if(maxNumber >= award.getDailyMax()){
                        continue;
                    }
                    int random = RandomUtil.randomInt(0, MAX_RANDOM);
                    // 概率值
                    if ( random <= PROBABILITY ) {
                        prizeCacheDao.addPrize(award.getId());
                        ExMatchAwardEntity updateAward = new ExMatchAwardEntity();
                        updateAward.setId(award.getId());
                        updateAward.setStock(award.getStock() - 1);
                        exMatchAwardDao.updateById(updateAward);
                        // 刷新map中的值
                        maxNumber = maxNumber + 1;
                        dateMap.put(dateKey, maxNumber);
                        break;
                    }
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }, interval * 1000);
    }

    @Override
    public List<String> getLotteryAnnouncement() {
        return lotteryCacheDao.getAnnouncement();
    }

    @Override
    public LotteryBO startLottery(Long userId) {
        // 抽奖机会
        ExMatchUserEntity user = exMatchUserDao.selectById(userId);
        if (user == null) {
            throw new ServiceException("用户不存在");
        }
        if (exMatchUserDao.decrLotteryToken(userId) == 0) {
            throw new ServiceException(ResultEnum.LOTTERY_LIMIT);
        }

        // 抽奖
        Long awardId = lotteryAlgorithm(userId);
        if (awardId == 0) {
            return new LotteryBO().setLucky(awardId);
        }

        // 插入中奖记录
        ExMatchAwardRecordEntity awardRecord = new ExMatchAwardRecordEntity();
        awardRecord.setUserId(userId);
        awardRecord.setAwardId(awardId);
        exMatchAwardRecordDao.insert(awardRecord);

        // 缓存公告
        ExMatchAwardEntity award = exMatchAwardDao.selectById(awardRecord.getAwardId());
        if (award != null) {
            lotteryCacheDao.addAnnouncement(String.format(ANNOUNCEMENT_PATTERN,
                    user.getUsername(), award.getAwardName()));
        }

        return new LotteryBO().setLucky(awardId);
    }

    private Long lotteryAlgorithm(Long userId) {
        ExMatchAwardRecordEntity awardRecord = exMatchAwardRecordDao.selectOne(new QueryWrapper<ExMatchAwardRecordEntity>()
                .eq("user_id", userId).last("limit 1"));
        if (awardRecord != null) {
            return 0L;
        }

        String awardId = prizeCacheDao.getPrize();
        if (awardId == null) {
            return 0L;
        }
        return Long.valueOf(awardId);
    }

    @Override
    public String getLotteryRecord(Long userId) {
        ExMatchAwardRecordEntity awardRecord = exMatchAwardRecordDao.selectOne(new QueryWrapper<ExMatchAwardRecordEntity>()
                .eq("user_id", userId).last("limit 1"));
        if (awardRecord != null) {
            ExMatchAwardEntity award = exMatchAwardDao.selectById(awardRecord.getAwardId());
            if (award != null) {
                return award.getAwardName();
            }
        }
        return null;
    }

    @Override
    public void exportPrize() {
        List<PrizeVO> prizeVOList = getPrize();

        List<Map<String, Object>> excelList = new ArrayList<>();
        Map<String, Object> header = new HashMap<>();
        List<ExMatchAwardEntity> exMatchAwardEntities = exMatchAwardDao.selectList(new QueryWrapper<>());
        Map<Long, ExMatchAwardEntity> awardEntityMap = exMatchAwardEntities.stream()
                .collect(Collectors.toMap(ExMatchAwardEntity::getId, Function.identity()));

        String sheet = "中奖信息";
        header.put("sheetName", sheet);
        excelList.add(header);

        for (PrizeVO prizeVO : prizeVOList) {
            Map<String, Object> line = new HashMap<>();
            line.put("username", prizeVO.getUsername());
            line.put("prize", awardEntityMap.get(prizeVO.getAwardId()).getAwardName());
            line.put("phone", prizeVO.getPhone());
            line.put("address", prizeVO.getAddress());
            line.put("createTime", DateUtil.format(prizeVO.getCreateTime(),"yyyy-MM-dd"));
            excelList.add(line);
        }

        String[] columnNames = {"姓名", "奖品", "联系方式", "中奖地址", "中奖日期"};
        String[] keys = {"username", "prize", "phone", "address", "createTime"};
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
        ExcelsUtil.ExcelSingleOutputStream(excelList, keys, columnNames, sheet, request, response);
    }

    private List<PrizeVO> getPrize() {

        List<PrizeBO> prizeBOList = exMatchAwardRecordDao.getPrize();

        List<PrizeVO> prizeVOList = new ArrayList<>();
        for (PrizeBO prizeBO : prizeBOList) {
            PrizeVO prizeVO = new PrizeVO().setUsername(prizeBO.getUsername())
                    .setPhone(prizeBO.getPhone())
                    .setAwardId(prizeBO.getAwardId())
                    .setCreateTime(prizeBO.getCreateTime());

            if (StringUtils.isNotBlank(prizeBO.getProvince()) && StringUtils.isNotBlank(prizeBO.getCity()) &&
                    StringUtils.isNotBlank(prizeBO.getRegion()) && StringUtils.isNotBlank(prizeBO.getAddress())) {
                if (StringUtils.equals(prizeBO.getProvince(), prizeBO.getCity())) {
                    prizeVO.setAddress(StringUtils.join(Lists.newArrayList(
                            prizeBO.getCity(), prizeBO.getRegion(), prizeBO.getAddress()), "，"));
                } else {
                    prizeVO.setAddress(StringUtils.join(Lists.newArrayList(prizeBO.getProvince(),
                            prizeBO.getCity(), prizeBO.getRegion(), prizeBO.getAddress()), "，"));
                }
            }

            prizeVOList.add(prizeVO);
        }

        return prizeVOList;
    }

}
